package org.shiro.filters;

import cn.hutool.core.util.ObjectUtil;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.cleaver.spring.application.ApplicationContextUtil;
import org.cleaver.spring.utils.servlet.ServletUtil;
import org.shiro.config.ApplicationInitialize;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

import static org.shiro.basic.exceptions.handler.ShiroExceptionHandler.SYSTEM_USER_NOT_LOGIN;

/**
 * 重写Shiro表单拦截器
 *
 * @author LiKun
 * @date 2021/11/2 10:15
 */
public class ShiroAuthenticationFilter extends FormAuthenticationFilter {
    private static final String REQUEST_OPTIONS = "OPTIONS";
    private static final Logger logger = LoggerFactory.getLogger(ShiroAuthenticationFilter.class);

    private volatile ApplicationInitialize applicationInitialize;

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        logger.info("开始校验请求，路径为:{}", this.getPathWithinApplication(request));

        String path = this.getPathWithinApplication(request);
        return this.judgeRequestMethod(request) || (this.getApplicationInitialize()
                .judgePathRequiredAuthentication(path) && super.isAccessAllowed(request, response, mappedValue));
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        logger.info("请求{}不被允许通过", this.getPathWithinApplication(request));
        ServletUtil.write(SYSTEM_USER_NOT_LOGIN, (HttpServletResponse) response);
        return false;
    }

    /*-------------------------------------------------private method-------------------------------------------------*/

    /**
     * 判断给定的请求是否为 OPTIONS 类型请求
     *
     * @param servletRequest 网络请求
     *
     * @return 判断结果
     */
    private boolean judgeRequestMethod(ServletRequest servletRequest) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String method = request.getMethod().toUpperCase().intern();
        return REQUEST_OPTIONS.equals(method);
    }

    /**
     * 获取ApplicationInitialize；如果ApplicationInitialize不存在则初始化，否则直接返回
     *
     * @return ApplicationInitialize
     */
    private ApplicationInitialize getApplicationInitialize() {
        if (ObjectUtil.isNull(this.applicationInitialize)) {
            synchronized (ShiroAuthenticationFilter.class) {
                if (ObjectUtil.isNull(this.applicationInitialize)) {
                    this.applicationInitialize = ApplicationContextUtil.getBean(ApplicationInitialize.class);
                }
            }
        }
        return this.applicationInitialize;
    }
}
